package com.nononsenseapps.feeder.ui.reader.sample

import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.graphics.drawable.Drawable
import android.os.Build
import android.os.Bundle
import android.text.Spanned
import android.view.*
import android.widget.*
import androidx.appcompat.widget.ShareActionProvider
import androidx.core.text.BidiFormatter
import androidx.core.view.MenuItemCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.*
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.billy.android.swipe.SmartSwipe
import com.billy.android.swipe.SmartSwipeWrapper
import com.billy.android.swipe.SwipeConsumer
import com.billy.android.swipe.consumer.BezierBackConsumer
import com.billy.android.swipe.listener.SimpleSwipeListener
import com.bumptech.glide.RequestBuilder
import com.bumptech.glide.RequestManager
import com.bumptech.glide.load.DecodeFormat
import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.request.target.Target
import com.flask.colorpicker.ColorPickerView
import com.flask.colorpicker.builder.ColorPickerDialogBuilder
import com.gitee.wsl.common.ui.theme.inject.theme
import com.gitee.wsl.android.ext.ApplicationInit.application
import com.gitee.wsl.android.ext.math.sp
import com.lxj.xpopup.XPopup
import com.lxj.xpopup.core.BubbleAttachPopupView
import com.lxj.xpopup.interfaces.XPopupCallback
import com.lzx.starrysky.SongInfo
import com.lzx.starrysky.StarrySky
import com.nononsenseapps.feeder.cache.LocalFileCacheManager
import com.nononsenseapps.feeder.db.ARG
import com.nononsenseapps.feeder.db.DEFAULT
import com.nononsenseapps.feeder.db.entity.PreLoadStateHelper
import com.nononsenseapps.feeder.db.room.*
import com.nononsenseapps.feeder.imageloader.ImageLoaderManager.xPopupImageLoader
import com.nononsenseapps.feeder.glide.GlideImageLoader
import com.nononsenseapps.feeder.db.view.PreviewItem
import com.nononsenseapps.feeder.model.htmlParser
import com.nononsenseapps.feeder.theme.appTheme
import com.nononsenseapps.feeder.tts.textInterface.FeedItemWithFeedTextInterface
import com.nononsenseapps.feeder.ui.R
import com.nononsenseapps.feeder.ui.base.markAsReadAndNotified
import com.nononsenseapps.feeder.ui.common.htmlview.converter.toSpannedWithNoImages
import com.nononsenseapps.feeder.ui.common.video.GPVideoPlayerActivity
import com.nononsenseapps.feeder.ui.feed.ActionCallBackAdapter
import com.nononsenseapps.feeder.ui.feed.FeedItemListAdapter
import com.nononsenseapps.feeder.ui.feed.SampleFeedItemSelectCenterPopup
import com.nononsenseapps.feeder.ui.reader.FeedItemViewModel
import com.nononsenseapps.feeder.util.*
import com.nononsenseapps.feeder.util.NlpUtils.CHINA_HK
import fr.gaulupeau.apps.Poche.tts.TtsConverter
import fr.gaulupeau.apps.Poche.tts.source.EmptyTtsHost
import fr.gaulupeau.apps.Poche.tts.source.TextInterface
import io.noties.markwon.base.Markwon
import io.noties.markwon.base.core.CorePlugin
import io.noties.markwon.base.core.spans.HeadingSpan
import io.noties.markwon.base.html.HtmlFormatPlugin
import io.noties.markwon.base.image.*
import io.noties.markwon.base.image.load.ExtGlideStore
import io.noties.markwon.base.image.size.ImageAutoSizePreLoadResolverDef
import io.noties.markwon.base.image.span.AsyncDrawableSpan
import io.noties.markwon.plugin.callout.CalloutPlugin
import io.noties.markwon.plugin.graphviz.GraphvizPlugin
import io.noties.markwon.plugin.highlight.HighlightPlugin
import io.noties.markwon.plugin.inline.MarkwonInlineParserPlugin
import io.noties.markwon.plugin.latex.JLatexMathPlugin
import io.noties.markwon.plugin.linkify.LinkifyPlugin
import io.noties.markwon.plugin.strikethrough.StrikethroughPlugin
import io.noties.markwon.plugin.syntax.Prism4jThemeDefault
import io.noties.markwon.plugin.syntax.SyntaxHighlightPlugin
import io.noties.markwon.plugin.tables.TablePlugin
import io.noties.markwon.plugin.tasklist.TaskListPlugin
import io.noties.markwon.plugin.typogram.TypogramPlugin
import io.noties.markwon.prism4j.MyGrammarLocator
import io.noties.markwon.theme.markwonTheme
import io.noties.prism4j.Prism4j
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.joda.time.DateTimeZone
import timber.log.Timber
import java.util.*

@SuppressLint("UseCompatLoadingForDrawables")
abstract class AbstractReaderFragment :  AbstractBaseReaderFragment() {

    protected lateinit var similarityList: RecyclerView
    protected lateinit var simalarityListAdapter: FeedItemListAdapter
    protected  val  feedItemViewModel  by viewModels<FeedItemViewModel>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setHasOptionsMenu(true)
        arguments?.getLong(ARG.FEED_ITEM_ID)?.let {
            if (it > ID_UNSET) {
                feedItemViewModel.notificationRead(it)
                markAsReadAndNotified(requireContext(), lifecycleScope, it)
            }
        }
    }

        override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                                  savedInstanceState: Bundle?): View {
            // Inflate the layout for this fragment
            val rootView = super.onCreateView(inflater, container, savedInstanceState)

            storySpeak.setOnClickListener {
                ttsSpeak(buildTextInterface())
            }

            itemFeatureView.setOnClickListener {
                XPopup.Builder(requireContext()).asCustom(SampleFeedItemSelectCenterPopup(requireContext(),feedItemViewModel.listItemFeatureList(), itemDao = feedItemViewModel.getItemDao(), actionCallback = object:ActionCallBackAdapter(lifecycleScope){
                    override fun onItemClick(item: PreviewItem) {
                        feedItemViewModel.updateItem(item.id)
                    }
                })).show()
            }


            similarityList=rootView.findViewById(R.id.similarity_list)
            similarityList.layoutManager=LinearLayoutManager(requireActivity())

            simalarityListAdapter= FeedItemListAdapter(requireActivity(), itemDao = feedItemViewModel.getItemDao() , viewModel=true, actionCallback =object: ActionCallBackAdapter(lifecycleScope){
                override fun onItemClick(item: PreviewItem) {
                    if(globalViewModel.isNextID(item.id))
                        moveNext()
                    else
                        feedItemViewModel.updateItem(item.id)
                }
            })

            similarityList.adapter=simalarityListAdapter

            titleTextView.setOnClickListener {
                openInWebView()
            }

            bindViewModel()

            SmartSwipe.wrap(scrollBodyView)
                .addConsumer(BezierBackConsumer())
                .enableHorizontal()
                .addListener(object : SimpleSwipeListener() {
                    override fun onSwipeOpened(wrapper: SmartSwipeWrapper?, consumer: SwipeConsumer?, direction: Int) {
                        Timber.d("swipe direction:%d", direction)
                        when (direction) {
                            SwipeConsumer.DIRECTION_LEFT -> movePref()
                            SwipeConsumer.DIRECTION_RIGHT -> moveNext()
                        }
                    }
                })

            if(!feedItemViewModel.isNotification)
                moveToId(globalViewModel.currentItemId())

            //bodyTextView.customSelectionActionModeCallback = ContextBasedRangeFormattingCallback(bodyTextView)
            initTheme()
            return buildSwipeView(rootView)
        }

       //val colors= listOf<Long>(ChineseColor.Other.CHAHE,ChineseColor.Other.DANSONGYAN,ChineseColor.Other.MISE,ChineseColor.Other.OUHE)

    protected fun updateScrollViewPostion() {
        Timber.d("restore read postion scrollY:${feedItemViewModel.readLastScrollLine}")
        scrollView.scrollTo(0, feedItemViewModel.readLastScrollLine)
    }

    open fun buildSwipeView(inView: View):View{
            return inView
     }

    override fun initTheme() {
        super.initTheme()
        itemTagsView.theme(appTheme)
    }

    @SuppressLint("SetTextI18n")
        private fun bindViewModel(){
            lifecycleScope.launch {
                lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED){

                    feedItemViewModel.liveItemFlow.collect { rssItem ->

                        if( rssItem.id == ID_UNSET ) return@collect

                        simalarityListAdapter.clearItems()

                        //setViewTitle()
                        if (rssItem.title.isEmpty()) {
                            titleTextView.text = rssItem.plainTitle
                        } else {
                            titleTextView.text = toSpannedWithNoImages(requireContext(), rssItem.title, rssItem.feedUrl)
                        }

                        mFeedTitleTextView.text = rssItem.feedDisplayTitle

                        rssItem.pubDate.let { pubDate ->
                            rssItem.author.let { author ->
                                when {
                                    author == null && pubDate != null ->
                                        mAuthorTextView.text = getString(R.string.on_date,
                                            pubDate.withZone(DateTimeZone.getDefault())
                                                .toString(dateTimeFormat))
                                    author != null && pubDate != null ->
                                        mAuthorTextView.text = getString(R.string.by_author_on_date,
                                            // Must wrap author in unicode marks to ensure it formats
                                            // correctly in RTL
                                            unicodeWrap(author),
                                            pubDate.withZone(DateTimeZone.getDefault())
                                                .toString(dateTimeFormat))
                                    else -> mAuthorTextView.visibility = View.GONE
                                }
                            }
                        }

                        storyWordCount.text = rssItem.wordCount.toString() + " word"

                        rssItem.feature.let{ feature ->
                            when{
                                feature != null -> feedItemViewModel.feedItemFeature?.let {
                                    itemFeatureView.visibility = View.VISIBLE
                                    itemFeatureView.text = it.title
                                }
                                else -> itemFeatureView.visibility = View.GONE
                            }
                        }
                        rssItem.itemTags.let{ tag ->
                            when{
                                tag.isNotEmpty() -> {
                                    itemTagsView.visibility = View.VISIBLE
                                    itemTagsView.setLabels(tag.split(","))
                                } else -> itemTagsView.visibility = View.GONE
                            }
                        }

                        bodyTextView.setMarkdownString(rssItem.description)

                        // Update state of notification toggle
                        requireActivity().invalidateOptionsMenu()

                        val similarityItemList= ArrayList<PreviewItem>()
                        similarityItemList.addAll(feedItemViewModel.similarityItems)
                        globalViewModel.nextItem()?.let {
                            if(!similarityItemList.contains(it))
                                similarityItemList.add(0,it)
                        }
                        simalarityListAdapter.submitList(similarityItemList)

                        if(feedItemViewModel.readLastScrollLine == 0)
                            bodyTextView.parentScrollChanged(0,feedItemViewModel.readLastScrollLine,-1,windowHeight)
                    }
                }
            }
        }

        protected fun moveNext() {
            if(feedItemViewModel.isNotification)
                return
            val nextId = globalViewModel.nextItemId()
            if (nextId == null) {
                context?.makeToast("到底了。。")
            } else {
                Timber.d("to go next:$nextId")
                moveToId(nextId)
            }
        }

        protected fun movePref() {
            if(feedItemViewModel.isNotification)
                return
            val nextId = globalViewModel.prefItemId()
            if (nextId == null) {
                requireContext().makeToast("到头了。。")
            } else {
                Timber.d("to go pref:$nextId")
                moveToId(nextId)
            }
        }

        protected fun moveToId(id: Long) {
            Timber.d("move to id:$id\t")
            if(feedItemViewModel.currentItemId() == id) {
                Timber.d("move to the same id:$id\t, ignore it")
                return
            }

            if (id > ID_UNSET) {
                markAsReadAndNotified(requireContext(),lifecycleScope,id)
                feedItemViewModel.updateItem(id)
                updateScrollViewPostion()
            }
        }

    protected fun getCurrentFeedItem(): FeedItemWithFeed {
            return feedItemViewModel.getCurrentFeedItem()
        }

    override fun initOutlineListView(listView: ListView) {
        feedItemViewModel.liveItemFlow.asLiveData().observe(viewLifecycleOwner) {
            val spanned = bodyTextView.text as Spanned
            val text = bodyTextView.text

            val headerAdapter = HeaderAdapter(requireContext(), 0, spanned) {
                // 获取所在的行数
                val lineOffset =
                    bodyTextView.layout.getLineForOffset(spanned.getSpanStart(it));
                scrollView.scrollTo(
                    0,
                    getHeadHight() + bodyTextView.layout.getLineTop(lineOffset)
                )
            }

            if (text is Spanned) {
                val headingSpans =
                    text.getSpans(0, text.length, HeadingSpan::class.java)
                headerAdapter.addAll(headingSpans.asList().reversed())
            }

            listView.adapter = headerAdapter
        }
    }

    abstract fun openInWebView()

        override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
            inflater.inflate(R.menu.reader, menu)

            // Locate MenuItem with ShareActionProvider
            val shareItem = menu.findItem(R.id.action_share)

            // Fetch and store ShareActionProvider
            val shareActionProvider = MenuItemCompat.getActionProvider(shareItem) as ShareActionProvider

            // Set intent
            getCurrentFeedItem().let { item ->

                val shareIntent = Intent(Intent.ACTION_SEND)
                shareIntent.type = "text/plain"
                shareIntent.putExtra(Intent.EXTRA_SUBJECT, titleTextView.text)
                shareIntent.putExtra(Intent.EXTRA_TEXT, item.link)
                shareActionProvider.setShareIntent(shareIntent)

                // Show/Hide enclosure
                menu.findItem(R.id.feed_enclosure).isVisible = item.enclosureLink != null
                // Add filename to tooltip

                //menu.findItem(R.id.action_load_full_page).isVisible = item.link?.startsWith("http")==true

                item.enclosureFilename?.let { fileName ->
                    menu.findItem(R.id.action_open_enclosure).title = fileName

                    LocalFileCacheManager.getDownLoadState(requireContext(),item.enclosureLink!!).let { state ->
                        menu.findItem(R.id.action_pre_download_enclosure).isVisible =
                            PreLoadStateHelper.isStartAble(state)
                        menu.findItem(R.id.action_pause_pre_download_enclosure).isVisible=
                            PreLoadStateHelper.isPauseAble(state)
                        menu.findItem(R.id.action_resume_pre_download_enclosure).isVisible=
                            PreLoadStateHelper.isResumeAble(state)
                        menu.findItem(R.id.action_cancel_pre_download_enclosure).isVisible =
                            PreLoadStateHelper.isCancelAble(state)
                        menu.findItem(R.id.action_del_pre_download_enclosure).isVisible =
                            PreLoadStateHelper.isDelAble(state)
                    }
                }
            }

            // Don't forget super call here
            super.onCreateOptionsMenu(menu, inflater)
        }




    override fun onPrepareOptionsMenu(menu: Menu) {
            getCurrentFeedItem().let{
                if (it.feedId == FAVORITE_FEED_ID) {
                    menu.findItem(R.id.action_mark_as_favorite)?.isVisible = false
                }
            }
            super.onPrepareOptionsMenu(menu)
        }

        override fun onOptionsItemSelectedWithFab(id :Int, view: View?) {
            when (id) {
                R.id.action_tts -> {
                    ttsSpeak(buildTextInterface())
                }

                R.id.action_open_in_browser -> {
                    val link = getCurrentFeedItem().link
                    if (link != null) {
                        openLinkInBrowser(requireContext(), link)

                    }
                }

                R.id.action_mark_as_favorite -> {
                    markAsFavorite()
                }

                R.id.action_view_outline -> {
                    if(view!=null)
                        showOutLinePop(view)
                    else
                        showOutLine()
                }

                R.id.action_tag -> {
                    editTags()
                }

                R.id.action_text_size_up -> {
                    PrefUtils.setReadTextSize(requireContext(),(bodyTextView.textSize / resources.displayMetrics.density) * 1.05f)
                    initReadTextSize()
                }

                R.id.action_text_size_down ->{
                    PrefUtils.setReadTextSize(requireContext(),(bodyTextView.textSize / resources.displayMetrics.density) * 0.9f)
                    initReadTextSize()
                }
            }
        }


        override fun onOptionsItemSelected(menuItem: MenuItem): Boolean {
            return when (menuItem.itemId) {
                R.id.action_tts -> {
                    ttsSpeak(buildTextInterface())
                    true
                }

                R.id.action_open_in_webview -> {
                    // Open in web view
                    openInWebView()
                    true
                }
                R.id.action_open_in_browser -> {
                    val link = getCurrentFeedItem().link
                    if (link != null) {
                        openLinkInBrowser(requireContext(), link)
                    }
                    true
                }
                R.id.action_open_enclosure -> {
                    getCurrentFeedItem().let {
                        openEnclosure(it.enclosureLink!!, titleTextView.text.toString(), it.feedTitle, it.imageUrl)
                    }
                    true
                }
                R.id.action_pre_download_enclosure->{
                    getCurrentFeedItem().let {
                        it.enclosureLink?.let { it1 -> downLoadEnclosure(it.id, it1) }
                    }
                    true
                }
                R.id.action_pause_pre_download_enclosure->{
                    getCurrentFeedItem().let {
                        it.enclosureLink?.let { it1 -> pauseDownLoadEnclosure(it.id, it1) }
                    }
                    true
                }
                R.id.action_resume_pre_download_enclosure->{
                    getCurrentFeedItem().let {
                        it.enclosureLink?.let { it1 -> resumeDownLoadEnclosure(it.id, it1) }
                    }
                    true
                }

                R.id.action_cancel_pre_download_enclosure->{
                    getCurrentFeedItem().let {
                        it.enclosureLink?.let { it1 -> cancelDownLoadEnclosure(it.id, it1) }
                    }
                    true
                }
                R.id.action_del_pre_download_enclosure->{
                    getCurrentFeedItem().let {
                        it.enclosureLink?.let { it1 -> delDownLoadEnclosure(it.id, it1) }
                    }
                    true
                }
                R.id.action_mark_as_unread -> {
                    lifecycleScope.launch(Dispatchers.Default) {
                        getCurrentFeedItem().let { feedItemWithFeed -> feedItemDao.markAsRead(feedItemWithFeed.id, unread = true) }
                    }
                    true
                }
                R.id.action_change_background_color -> {
                    ColorPickerDialogBuilder
                        .with(requireContext(), R.style.ColorPickerDialogTheme)
                        .setTitle(getString(R.string.change_background_color))
                        .initialColor(PrefUtils.getReadBackgroundColor(requireActivity()))
                        .wheelType(ColorPickerView.WHEEL_TYPE.FLOWER)
                        .density(12)
                        .setOnColorSelectedListener {
                            fragmentReader.setBackgroundColor(it)
                        }
                        .setPositiveButton(getString(R.string.action_enter)) { _, lastSelectedColor, _ ->
                            fragmentReader.setBackgroundColor(lastSelectedColor)
                            PrefUtils.setReadBackgroundColor(requireActivity(), lastSelectedColor)
                        }
                        .setNegativeButton(getString(R.string.action_cancel)) { _, _ ->
                            fragmentReader.setBackgroundColor(PrefUtils.getReadBackgroundColor(requireActivity()))
                        }
                        .build()
                        .show()
                    true
                }
                R.id.action_mark_as_favorite -> {
                    markAsFavorite()
                    true
                }
                R.id.action_view_code -> {
                    // Open in web view
                    navigateToCodeViewFragment()
                    true
                }
                R.id.action_tag -> {
                    editTags()
                    true
                }
                R.id.action_view_outline -> {
                    showOutLine()
                    true
                }
                R.id.action_cn_to_tc -> {
                    feedItemViewModel.tranLanguage(Locale.SIMPLIFIED_CHINESE,CHINA_HK)
                    true
                }
                R.id.action_tc_to_cn -> {
                    feedItemViewModel.tranLanguage(CHINA_HK,Locale.SIMPLIFIED_CHINESE)
                    true
                }
                R.id.action_load_full_page -> {
                    feedItemViewModel.loadFullText()
                    true
                }
                R.id.action_format_body -> {
                    feedItemViewModel.formatBody()
                    true
                }
                R.id.action_change_theme -> {
                    changeTheme()
                    true
                }
                else -> super.onOptionsItemSelected(menuItem)
            }
        }


    @OptIn(InternalCoroutinesApi::class)
        fun editTags(){
            lifecycleScope.launch(Dispatchers.Default) {
                getCurrentFeedItem().let { item ->
                    val itemTags= mutableListOf<String>()
                    itemTags.addAll(NlpUtils.buildTags(item.plainTitle))
                    //itemTags.addAll(globalViewModel.createCurrentListItemsTags())
                    withContext(Dispatchers.Main) {
                        popItemTagsEdit(item.id, item.itemTags.split(","), itemTags) { _, tagStr ->
                            item.itemTags = tagStr.joinToString(",")
                        }
                    }
                }
            }
        }


    fun markAsFavorite(){
            getCurrentFeedItem().let { feedItemWithFeed ->
                if(feedItemWithFeed.feedId!= FAVORITE_FEED_ID) {
                    lifecycleScope.launch(Dispatchers.Default) {
                        feedItemDao.markAsFavorite(feedItemWithFeed.id, tag = feedItemWithFeed.tag)
                        feedItemWithFeed.feedId=FAVORITE_FEED_ID
                        withContext(Dispatchers.Main) {
                            requireActivity().makeToast(getString(R.string.feed_favorite_success))
                        }
                    }
                }else
                    requireActivity().makeToast(getString(R.string.feed_favorite_already))
            }
        }

    fun buildTextInterface(): TextInterface = FeedItemWithFeedTextInterface(
        TtsConverter(context?.applicationContext),
        EmptyTtsHost(),
        feedItemViewModel.getCurrentFeedItem()
    )

     override fun urlClickListener(link: String) {
        when (PrefUtils.shouldOpenLinkWith(requireContext())) {
            DEFAULT.OPEN_TYPE.PREF_VAL_OPEN_WITH_WEBVIEW -> {
                navigateToWebViewFragment(link)
            }
            else -> {
                openLinkInBrowser(requireContext(), link)
            }
        }
    }

    abstract fun navigateToWebViewFragment(urlLink: String)

    override fun previousArticle(): Boolean {
            return false
        }

     override fun nextArticle(): Boolean {
            return false
        }
}



fun Fragment.openEnclosure(link: String, title: String, feedName: String, imageUrl: String?){
    var mineType = MediaFileUtil.getFileType(link)
    if (mineType == null) {
        mineType= MediaFileUtil.getFileType(MediaFileUtil.removeParams(link))
    }
    val realLink = LocalFileCacheManager.getDownLoadUrl(requireContext(),link)
    if (mineType != null) {
        var breaked=false
        if(PrefUtils.shouldLoadVideoInside(activity!!)) {
            if(MediaFileUtil.isAudioFileType(mineType.fileType)) {
                showAudioPreview(activity!!, realLink, title, mineType.mimeType, feedName, imageUrl
                        ?: "")
                breaked=true
            }else if(MediaFileUtil.isVideoFileType(mineType.fileType)) {
                showVideoPreview(activity!!, realLink,title,imageUrl)
                breaked=true
            }
        }
        if(!breaked) {
            if (MediaFileUtil.isImageFileType(mineType.fileType)) {
                showImagePreview(requireContext(), realLink)
            }else
               IntentUtils.startActivityByUrl(activity!!, realLink, title, mineType.mimeType)
        }
    } else {
        openLinkInBrowser(requireContext(), realLink)
    }
}


fun Fragment.unicodeWrap(text: String): String =
        BidiFormatter.getInstance(getLocale()).unicodeWrap(text)

fun Fragment.getLocale(): Locale? =
        context?.getLocale()

fun Context.unicodeWrap(text: String): String =
        BidiFormatter.getInstance(getLocale()).unicodeWrap(text)

fun Context.getLocale(): Locale =
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            resources.configuration.locales[0]
        } else {
            @Suppress("DEPRECATION")
            resources.configuration.locale
        }

fun showImagePreview(context: Context, span: Spanned?, url: String, xPopupCallback: XPopupCallback? = null) {
        Timber.d("image clicked id:" + url)
        var index = 0
    span?.getSpans(0, span.length, AsyncDrawableSpan::class.java)?.filter { it.drawable.destination.isNotEmpty() }?.sortedBy { span.getSpanStart(it) }?.mapIndexed { i, asyncDrawableSpan ->
        if (url == asyncDrawableSpan.drawable.destination)
            index = i
        asyncDrawableSpan.drawable.destination
    }?.let { urlList->
        Timber.d("image preview size:%d\tindex:%d", urlList.size, index)
        showImagePreview(context, index, urlList, xPopupCallback = xPopupCallback)
    }
}

fun showImagePreview(context: Context, url: String, srcView: ImageView? = null, xPopupCallback: XPopupCallback? = null) {
    if(xPopupCallback!=null)
        XPopup.Builder(context).setPopupCallback(xPopupCallback).asImageViewer(srcView, url, xPopupImageLoader).show()
    else
        XPopup.Builder(context).asImageViewer(srcView, url, xPopupImageLoader).show()
}

fun showImagePreview(context: Context, currentPosition: Int, urls: List<String>, srcView: ImageView? = null, xPopupCallback: XPopupCallback? = null) {
    if(xPopupCallback!=null)
        XPopup.Builder(context).setPopupCallback(xPopupCallback).asImageViewer(srcView, currentPosition, urls, null, xPopupImageLoader).show()
    else
        XPopup.Builder(context).asImageViewer(srcView, currentPosition, urls, null, xPopupImageLoader).show()
}

fun showVideoPreview(activityMain: Activity, url: String, title: String="", imageUrl: String?=null) {
    Timber.d("video clicked id:%s", url)
    GPVideoPlayerActivity.startActivity(activityMain, url,title,imageUrl)
}

fun showAudioPreview(activityMain: Activity, url: String, title: String, mimeType: String, feedName: String, imageUrl: String = ""){
    Timber.d("audio clicked id:%s", url)
    val info = SongInfo(songId = title,
            songUrl = url,
            songName = title,
            songCover = imageUrl)
    StarrySky.with().playMusicByInfo(info)
    activityMain.makeToast("play $title")
}

class HeaderAdapter(context:Context, resource:Int,val spanned:Spanned,val onClick:(HeadingSpan)->Unit) : ArrayAdapter<HeadingSpan>(context,resource) {
    override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
        val headText = TextView(parent.context)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            headText.setTextAppearance(R.style.TextAppearance_Reader_Body)
        }
        val currentSpan = getItem(position)!!
        val startSpanIndex = spanned.getSpanStart(currentSpan)
        val endSpanIndex = spanned.getSpanEnd(currentSpan)
        val stringText = "..".repeat(currentSpan.level - 1) + (spanned.subSequence(startSpanIndex, endSpanIndex).toString().replace("\n"," "))
        headText.text = stringText
        headText.setOnClickListener {
            onClick(currentSpan)
        }

        return headText
    }
}

@SuppressLint("ViewConstructor")
class CustomAttachPopup(context:Context, private val headerAdapter: HeaderAdapter): BubbleAttachPopupView(context){

    override fun getImplLayoutId():Int{
        return R.layout.float_reader_outline
    }

    override fun onCreate() {
        super.onCreate()

        findViewById<ListView>(R.id.iv_outline_head).apply {
            adapter = headerAdapter
        }

        findViewById<View>(R.id.iv_outline_toolbar).visibility=View.GONE
    }
}


val autoSizePreLoadResolverDef = ImageAutoSizePreLoadResolverDef(100, 1f)

@SuppressLint("CheckResult", "StaticFieldLeak")
val requestManager= GlideImageLoader.glide(application).apply {
    /*this.setDefaultRequestOptions(RequestOptions().apply {
        //format(DecodeFormat.PREFER_RGB_565)
        placeholder(R.drawable.placeholder_image_article_night)
    })*/
}

@SuppressLint("CheckResult", "StaticFieldLeak")
val preRequestManager=requestManager.apply {
    this.setDefaultRequestOptions(RequestOptions().apply {
        format(DecodeFormat.PREFER_RGB_565)
    })
}


val extGlideStore by lazy{
    object : ExtGlideStore {
        override fun preLoad(drawable: AsyncRecycleDrawable): RequestBuilder<Drawable> {
            return load(preRequestManager,drawable)
        }

        override val placeholder: Drawable?
            get() = application.getDrawable(R.drawable.placeholder_image_article_night)
        override val imagePreLoadSizeResolverDef: ImageAutoSizePreLoadResolverDef
            get() = autoSizePreLoadResolverDef

        override fun clearMemoryCache() {
            /*preRequestManager.setPauseAllRequestsOnTrimMemoryModerate(true)
            preRequestManager.onTrimMemory(ComponentCallbacks2.TRIM_MEMORY_MODERATE)*/
        }

        @SuppressLint("UseCompatLoadingForDrawables")
        override fun load(drawable: AsyncDrawable): RequestBuilder<Drawable> {
            return load(requestManager,drawable)
        }

        private fun load(requestManager:RequestManager,drawable: AsyncDrawable): RequestBuilder<Drawable> {
            //Timber.d("GlideStore requestManager load drawable:${drawable.destination}")
            if(drawable.destination.startsWith("drawable://")){
                // will handle URLs like `drawable://ic_account_24dp_white`
                val resourceId = application.resources.getIdentifier(
                    drawable.destination.substring("drawable://".length),
                    "drawable",
                    application.packageName
                )
                Timber.d("GlideStore requestManager load resourceId:$resourceId")

                return requestManager.load(resourceId)
            }

            val rect = autoSizePreLoadResolverDef.resolveImageSize(drawable)
            Timber.d("GlideStore requestManager drawable resolver is auto size:${autoSizePreLoadResolverDef.autoResolved} size maxWidth;${autoSizePreLoadResolverDef.maxWidth}\t")

            if (autoSizePreLoadResolverDef.autoResolved) {
                Timber.d("GlideStore requestManager autoResolved image size width:${drawable.imageSize?.width} height:${drawable.imageSize?.height} override size width:${rect.width()}\t height:${rect.height()}")
                return requestManager.load(drawable.destination)
                    .fitCenter()
                    .override(rect.width(), rect.height())
            }

            return requestManager.load(drawable.destination)
        }

        override fun cancel(target: Target<*>) {
            requestManager.clear(target)
        }
    }
}

val markwonTheme = markwonTheme(appTheme){
}

val markwon by lazy {
    Markwon.builder(application, markwonTheme)
        .usePlugin(CorePlugin.create())
        .usePlugin(HtmlFormatPlugin.create(htmlParser))
        .usePlugin(MarkwonInlineParserPlugin.create())
        .usePlugin(GlideImagesWithGifPlugin.create(extGlideStore))
        .usePlugin(SyntaxHighlightPlugin.create(
            Prism4j(MyGrammarLocator()),
            Prism4jThemeDefault.create()
           )
        )
        .usePlugin(JLatexMathPlugin.Companion.create(12f.sp))
        .usePlugin(TablePlugin.create())
        .usePlugin(LinkifyPlugin.create(true))
        .usePlugin(StrikethroughPlugin.create())
        .usePlugin(HighlightPlugin.create())
        .usePlugin(TaskListPlugin.create(markwonTheme.colorSchemeAndroid.primary,markwonTheme.colorSchemeAndroid.primary,markwonTheme.colorSchemeAndroid.primary))
        .usePlugin(TypogramPlugin.create())
        .usePlugin(CalloutPlugin.create())
        .usePlugin(GraphvizPlugin.create())
        .build()
}